package com.dly.blog.websocket;

import cn.hutool.core.util.StrUtil;
import com.dly.blog.content.RedisContent;
import com.dly.blog.domain.dto.GptResponseDto;
import com.dly.blog.domain.dto.MessageContentDto;
import com.dly.blog.domain.enums.ChatType;
import com.dly.blog.domain.ro.ChatGptRO;
import com.dly.blog.exception.ChatExceptionEnum;
import com.dly.blog.provider.user.UserProvider;
import com.dly.blog.service.ChatProxy;
import com.dly.blog.service.GptSessionContentService;
import com.dly.blog.service.GptSessionService;
import com.dly.blog.utile.JsonUtils;
import com.dly.blog.utile.RedisUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: DaiLiYu
 * @Date: 2023/7/24 9:29
 */
@ServerEndpoint("/chat/api/{userId}/{type}/{sessionId}")
@Service
@Slf4j
public class WebSocketServer {

    private static ConcurrentHashMap<String, Session> WEB_SOCKET_MAP = new ConcurrentHashMap<>();

    private static RedisUtils REDISUTILS;

    private static List<ChatProxy> CHATPROXIES;

    private static UserProvider USERPROVIDER;

    private static GptSessionContentService GPTSESSIONCONTENTSERVICE;

    private static GptSessionService GPTSESSIONSERVICE;

    @Autowired
    public void setRedisUtils(RedisUtils redisUtils){
        REDISUTILS = redisUtils;
    }

    @Autowired
    public void setChatProxies(List<ChatProxy> chatProxies){ CHATPROXIES = chatProxies; }

    @Autowired
    public void setUserProvider(UserProvider userProvider){USERPROVIDER = userProvider;}

    @Autowired
    public void setGptSessionContentService(GptSessionContentService gptSessionContentService){GPTSESSIONCONTENTSERVICE = gptSessionContentService;}

    @Autowired
    public void setGptSessionService(GptSessionService gptSessionService){GPTSESSIONSERVICE = gptSessionService;}

    @OnOpen
    public void open(Session session, @PathParam("userId") Long userId, @PathParam("type") String type, @PathParam("sessionId") String sessionId) throws IOException {
        log.info("userId:{}, type:{}, 正在连接", userId, type);
        Set<Long> set = REDISUTILS.getSet(RedisContent.ONLINE);
        //用户没有登入
        if(!set.contains(userId)){
            session.close(new CloseReason(CloseReason.CloseCodes.CLOSED_ABNORMALLY, "未经授权访问"));
        }
        log.info("userId:{}, type:{}, 连接成功", userId, type);
        WEB_SOCKET_MAP.put(userId.toString(), session);
    }

    @OnClose
    public void onClose(Session session, @PathParam("userId") String userId, @PathParam("type") String type, @PathParam("sessionId") String sessionId) {
        log.info("user:{}, 关闭了连接", userId);
    }

    @OnError
    public void onError(Session session, Throwable throwable, @PathParam("userId") String userId, @PathParam("type") String type, @PathParam("sessionId") String sessionId) {
        log.info("user:{}, 发送错误", userId);
    }

    @OnMessage
    public void onMessage(String message, Session session, @PathParam("userId") String userId, @PathParam("type") String type, @PathParam("sessionId") String sessionId){
        log.info("传过来的信息: {}", message);
        log.info("传过来的Session: {}", session);
        log.info("传过来的UserId: {}", userId);
        log.info("传过来的type: {}", type);
        log.info("穿过来的sessionId: {}", sessionId);
        //获取发送的消息
        ChatProxy chatProxy = getChatProxy(type);
        List<MessageContentDto> messageContentDtos = JsonUtils.readFromJsonString(message, new TypeReference<List<MessageContentDto>>() {});
        StringBuilder sb = new StringBuilder();
        chatProxy.send(messageContentDtos).mapNotNull(content -> {
            if (content.equals("[DONE]")) {
                return "";
            }
            GptResponseDto.Choices choices = JsonUtils.readFromJsonString(content, GptResponseDto.class).getChoices().get(0);
            //自己搜集数据
            if(Objects.nonNull(choices.getDelta().getContent())) sb.append(choices.getDelta().getContent());
            //由于单独传输容易丢失空格
            return JsonUtils.writeToJson(choices);
        }).subscribe(str-> send(userId, str),
                error->{
                    log.info("发送错误 补偿用户{}", userId);
                    USERPROVIDER.addFrequency(userId, 1);
                },
                ()->{
                    //执行完成释放资源 这里可以知道这次时正常完成的 保存一下数据
                    try {
                        log.info("本次已经完成释放资源");
                        //减少次数
                        USERPROVIDER.reducedFrequency(userId, 1);
                        //保存数据
                        ChatGptRO content = getContent(ChatType.valueOf(type), sb, messageContentDtos.get(messageContentDtos.size() - 1));
                        if(StrUtil.equals(sessionId,"none")){
                            GPTSESSIONSERVICE.createGptSession(userId, content);
                        }else {
                            GPTSESSIONCONTENTSERVICE.saveContent(userId, sessionId, content);
                        }
                        session.close();
                    } catch (IOException e) {
                        log.error("session 关闭失败");
                    }
                }
        );
    }

    public void send(String userId,String s) {
        try {
            Session session = WEB_SOCKET_MAP.get(userId);
            session.getBasicRemote().sendText(s);
        } catch (IOException e) {
            log.info("消息发送失败");
        }
    }

    public ChatProxy getChatProxy(String chatType) {
        return CHATPROXIES.stream()
                .filter(chatProxy -> chatProxy.matching(ChatType.valueOf(chatType)))
                .findAny()
                .orElseThrow(ChatExceptionEnum.CHAT_GET_CLIENT_ERROR::getChatException);
    }

    public ChatGptRO getContent(ChatType chatType,StringBuilder sb,MessageContentDto messageContentDto){
        ChatGptRO chatGptRO = new ChatGptRO();
        List<MessageContentDto> messageContentDtos = new ArrayList<>();
        messageContentDtos.add(messageContentDto);
        MessageContentDto messageContentDto1 = new MessageContentDto();
        messageContentDto1.setRole("assistant");
        messageContentDto1.setContent(sb.toString());
        messageContentDtos.add(messageContentDto1);
        chatGptRO.setMessages(messageContentDtos);
        chatGptRO.setChatType(chatType);
        return chatGptRO;
    }


}
